#include <QtCore/QDir>
#include <QtCore/QList>
#include <QtGui/QHeaderView>
#include <QtCore/QModelIndex>

#include "proeditormodel.h"
#include "profileevaluator.h"
#include "proitems.h"
#include "prowriter.h"

#include "scopelist.h"

using namespace Qt4ProjectManager::Internal;

QMap<QString, ProEditorModel *> &ScopeList::modelMap()
{
    static QMap<QString, ProEditorModel *> modelMap;
    return modelMap;
}

QMap<ProEditorModel *, int> &ScopeList::modelRefCount()
{
    static QMap<ProEditorModel *, int> modelRefCount;
    return modelRefCount;
}

ProEditorModel *ScopeList::aquireModel(const QString &fileName, const QString &contents)
{
    if (fileName.isEmpty())
        return 0;

    if (modelMap().contains(fileName))
    {
        ProEditorModel *value = modelMap().value(fileName);
        modelRefCount()[value] += 1;

        return value;
    }

    ProFileEvaluator::Option option;
    ProFileEvaluator reader(&option);
    ProFile *profile = new ProFile(fileName);

    if (!reader.queryProFile(profile, contents))
    {
        delete profile;
        return 0;
    }
    ProEditorModel *model = new ProEditorModel();
    model->setProFiles(QList<ProFile *>() << profile);
    modelMap().insert(fileName, model);
    modelRefCount().insert(model, 1);

    return model;
}

void ScopeList::releaseModel(ProEditorModel *editorModel)
{
    modelRefCount()[editorModel] -= 1;

    if (modelRefCount().value(editorModel) <= 0)
    {
        modelMap().remove(modelMap().key(editorModel));
        delete editorModel;
    }
}

ScopeList::ScopeList(QWidget *parent) : QTreeView(parent)
{
    header()->setVisible(false);
}

ScopeList::~ScopeList()
{
    Q_FOREACH (ProEditorModel *m, m_models.keys())
        ScopeList::releaseModel(m);
}

ProEditorModel *ScopeList::proEditorModel(const QString &fileName, const QString &contents)
{
    ProEditorModel *value = ScopeList::aquireModel(fileName, contents);

    if (!value)
        return 0;

    if (m_models.contains(value))
    {
        ScopeList::releaseModel(value);
    }
    else
    {
        m_models.insert(value, 0);
        m_proFileNameToModel.insert(fileName, value);
    }

    return value;
}

void ScopeList::showModel(const QString &proFileName, const QString &contents, bool enabled)
{
    ProEditorModel *modelHandle = proEditorModel(proFileName, contents);

    if (modelHandle)
    {
        ProScopeFilter *mf = filter(modelHandle);

        if (mf == model())
            return;

        Q_FOREACH (QString key, m_files.keys())
        {
            ensureVariable(key, modelHandle);
        }

        setModel(mf);
        expandAll();
    }
    setEnabled(enabled);
}

void ScopeList::ensureVariable(const QString &variable, ProEditorModel *model)
{
    QList<QModelIndex> vars = model->findVariables(QStringList(variable));

    if (vars.isEmpty())
    {
        vars = model->findBlocks();

        if (!vars.isEmpty())
        {
            ProVariable *var = new ProVariable(variable.toAscii(), model->proBlock(vars[0]));
            model->insertItem(var, model->rowCount(vars[0]), vars[0]);
        }
    }
}

void ScopeList::selectFirstVariable()
{
    if (ProScopeFilter *mf = qobject_cast<ProScopeFilter *>(model()))
    {
        ProEditorModel *m = qobject_cast<ProEditorModel*>(mf->sourceModel());

        QStringList vars = m_files.keys();

        for (int i = 0; i < vars.count(); ++i)
        {
            QList<QModelIndex> indexes = m->findVariables(QStringList(vars.at(i)));
            if (!indexes.isEmpty())
            {
                mf->setData(mf->mapFromSource(indexes.first()), QVariant((int)Qt::Checked), Qt::CheckStateRole);
            }
        }
    }
}

void ScopeList::addFile(const QString &file, const QString &var)
{
    QFileInfo info(file);
    m_files.insert(var, info);
    m_filenames << file;
}

bool ScopeList::search(const QString &proFileName, const QString &contents)
{
    bool found = false;
    ProEditorModel *m = proEditorModel(proFileName, contents);

    if (!m)
        return false;

    ProScopeFilter *mf = filter(m);

    QFileInfo profile(m->proFiles().first()->fileName());
    QList<QModelIndex> indexes = m->findVariables(m_files.keys());

    for (int i = 0; i < indexes.size(); ++i)
    {
        QModelIndex varindex = indexes.at(i);

        for (int j = m->rowCount(varindex) - 1; j >= 0; --j)
        {
            QModelIndex valindex = m->index(j,0,varindex);
            ProItem *item = m->proItem(valindex);

            if (!item || item->kind() != ProItem::ValueKind)
                continue;

            ProValue *val = static_cast<ProValue *>(item);
            QString absolutePathVal = QDir::toNativeSeparators(profile.dir().absoluteFilePath(val->value()));

            if (m_filenames.contains(absolutePathVal))
            {
                found = true;
                mf->setData(mf->mapFromSource(varindex), QVariant((int)Qt::Checked), Qt::CheckStateRole);

                break;
            }
        }
    }

    return found;
}

bool ScopeList::isChanged(const QString &proFileName)
{
    ProEditorModel *m = m_proFileNameToModel.value(proFileName);

    if (!m)
        return false;

    ProScopeFilter *mf = filter(m);

    return !mf->checkedIndexes().isEmpty();
}

QString ScopeList::removeFiles(const QString &proFileName)
{
    ProEditorModel *m = m_proFileNameToModel.value(proFileName);

    if (!m)
        return QString();

    QList<ProScopeFilter *> filters = m_models.values();

    for (int i = 0; i < filters.count(); ++i)
    {
        QFileInfo profile(m->proFiles().first()->fileName());
        QList<QModelIndex> indexes = filters.at(i)->checkedIndexes();

        for (int j = 0; j < indexes.size(); ++j)
        {
            QModelIndex varindex = indexes.at(j);

            for (int k = m->rowCount(varindex) - 1; k >= 0; --k)
            {
                QModelIndex valindex = m->index(k,0,varindex);
                ProItem *item = m->proItem(valindex);

                if (!item || item->kind() != ProItem::ValueKind)
                    continue;

                ProValue *val = static_cast<ProValue *>(item);
                QString absolutePathVal = QDir::toNativeSeparators(profile.dir().absoluteFilePath(val->value()));

                if (m_filenames.contains(absolutePathVal))
                {
                    m->removeItem(valindex);
                }
            }
        }
    }

    ProWriter writer;
    ProFile *proFile = m->proFiles().first();

    return writer.contents(proFile);
}

QString ScopeList::addFiles(const QString &proFileName)
{
    ProEditorModel *m = m_proFileNameToModel.value(proFileName);

    if (!m)
        return QString();

    QList<ProScopeFilter *> filters = m_models.values(m);

    Q_FOREACH(ProScopeFilter *mf, filters)
    {
        QFileInfo profile(m->proFiles().first()->fileName());

        QList<QModelIndex> indexes = mf->checkedIndexes();

        for (int i = 0; i<indexes.size(); ++i)
        {
            QModelIndex index = indexes.at(i);
            ProVariable *var = m->proVariable(index);

            if (!var)
                continue;

            QList<QFileInfo> files = m_files.values(var->variable());

            for (int j = 0; j < files.size(); ++j)
            {
                QString val = profile.dir().relativeFilePath(files.at(j).absoluteFilePath());
                m->insertItem(new ProValue(val.toUtf8(), var), 0, index);
            }
        }
    }

    ProWriter writer;
    ProFile *proFile = m->proFiles().first();

    return writer.contents(proFile);
}

ProScopeFilter *ScopeList::filter(ProEditorModel *model)
{
    if (!m_models.value(model))
    {
        ProScopeFilter *filter = new ProScopeFilter(this);
        filter->setCheckable(ProScopeFilter::Variable);
        filter->setSourceModel(model);
        filter->setVariableFilter(m_files.keys());
        m_models[model] = filter;
    }

    return m_models.value(model);
}
